package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.BeansException;
import cn.stimd.spring.beans.factory.*;
import cn.stimd.spring.beans.factory.config.BeanDefinition;
import cn.stimd.spring.beans.factory.config.BeanDefinitionHolder;
import cn.stimd.spring.beans.factory.config.DependencyDescriptor;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.inject.Provider;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {
    private static Class<?> javaxInjectProviderClass = null;
    //BeanDefinition名称
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    //BeanDefinition的缓存
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //手动注册的单例Bean
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
    //预设的依赖项，依赖解析时优先查找
    private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
    //为@Value和@Autowired注解提供支持
    private AutowireCandidateResolver autowireCandidateResolver = new QualifierAnnotationAutowireCandidateResolver();

    static {
        try {
            javaxInjectProviderClass =
                    ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - Provider interface simply not supported then.
        }
    }

    // ------------------------------- SingletonBeanRegistry接口

    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        super.registerSingleton(beanName, singletonObject);

        if(!this.beanDefinitionMap.containsKey(beanName)){
            this.manualSingletonNames.add(beanName);
        }
    }


    // ------------------------------- BeanDefinitionRegistry接口

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        this.beanDefinitionMap.put(beanName, beanDefinition);

        if(existingDefinition != null){
            clearMergedBeanDefinition(beanName);
        } else {
            beanDefinitionNames.add(beanName);
        }
    }

    @Override
    public void removeBeanDefinition(String beanName) {
        this.beanDefinitionMap.remove(beanName);
        this.beanDefinitionNames.remove(beanName);
        clearMergedBeanDefinition(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return this.beanDefinitionNames.contains(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        BeanDefinition definition = this.beanDefinitionMap.get(beanName);
        if(definition == null){
            throw new RuntimeException("没有找到Bean[" + beanName + "]的定义");
        }
        return definition;
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return StringUtils.toStringArray(this.beanDefinitionNames);
    }


    // ------------------------------- BeanFactory接口
    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        List<String> candidateNames = getBeanNamesForType(requiredType);
        if (candidateNames.size() == 1) {
            String beanName = candidateNames.get(0);
            return getBean(beanName, requiredType);
        }
        else if (candidateNames.size() > 1) {
            Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.size());
            for (String beanName : candidateNames) {
                if(containsSingleton(beanName)){
                    candidates.put(beanName, getBean(beanName, requiredType));
                }else{
                    candidates.put(beanName, getType(beanName));
                }
            }

            //获取标记了primary的Bean
            String candidateName = determinePrimaryCandidate(candidates);
            if(candidateName != null){
                Object beanInstance = candidates.get(candidateName);
                //直到确定之后才创建单例，避免提早创建的副作用
                if (beanInstance instanceof Class) {
                    beanInstance = getBean(candidateName, requiredType);
                }
                return (T) beanInstance;
            }
            throw new RuntimeException("无法获取唯一的单例Bean，候选者个数为" + candidates.size());
        }

        BeanFactory parent = getParentBeanFactory();
        if (parent != null) {
            return parent.getBean(requiredType);
        }
        return null;
    }

    @Override
    public List<String> getBeanNamesForType(Class<?> type) {
        List<String> result = new ArrayList<>();
        ResolvableType resolvableType = ResolvableType.forRawClass(type);

        //1. 检查通过BeanDefinition注册的单例
        for (String beanName : this.beanDefinitionNames) {
            RootBeanDefinition mbd = getMergedBeanDefinition(beanName);
            if(mbd.hasBeanClass() || !requiresEagerInitForType(mbd.getFactoryMethodName(), mbd)){
                boolean isFactoryBean = isFactoryBean(beanName, mbd);
                //1.1 假定是FactoryBean，优先检查是否与包装对象的类型匹配
                boolean matchFound = isTypeMatch(beanName, resolvableType);

                //1.2 FactoryBean的优先级低于包装的实例
                if (!matchFound && isFactoryBean) {
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                    matchFound = isTypeMatch(beanName, resolvableType);
                }

                if (matchFound) {
                    result.add(beanName);
                }
            }
        }

        //2. 检查手动注册的单例
        for (String beanName : this.manualSingletonNames) {
            if (isTypeMatch(beanName, resolvableType)) {
                result.add(beanName);
            }
        }
        return result;
    }

    //检查是否需要预先初始化Bean以决定其类型
    private boolean requiresEagerInitForType(String factoryBeanName, RootBeanDefinition definition) {
        return factoryBeanName != null && isFactoryBean(factoryBeanName, definition) && !containsSingleton(factoryBeanName);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        List<String> beanNames = getBeanNamesForType(type);
        Map<String, T> result = new LinkedHashMap<>(beanNames.size());
        for (String beanName : beanNames) {
            result.put(beanName, getBean(beanName, type));
        }
        return result;
    }


    // ------------------------------- AutowireCapableBeanFactory接口

    @Override
    public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException {
        //解析java.util.Optional类型（略）

        //依赖类型为ObjectFactory、ObjectProvider或javax.inject.Provider，此时Bean还不存在，无法获得实例。
        //此时返回的是包装对象，将实际的依赖解析延迟到真正使用的时候
        //Notice: Provider的实例必须存在，只是延迟获取，@Autowired(required=false)的实例可以不存在
        if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        }

        if(descriptor.getDependencyType() == javaxInjectProviderClass){
            return new Jsr330DependencyProvider(descriptor, requestingBeanName);
        }

        return doResolveDependency(descriptor);
    }


    //依赖解析的真正逻辑
    private Object doResolveDependency(DependencyDescriptor descriptor) {
        //1. 处理@Value注解
        Object value = this.autowireCandidateResolver.getSuggestedValue(descriptor);    //获取注解上的value属性值
        if (value != null) {
            if(value instanceof String){
                value = resolveEmbeddedValue((String) value);
            }
            return getTypeConverter().convertIfNecessary(value, descriptor.getDependencyType(), descriptor.getField());
        }

        //2. 集合类型依赖
        Object multipleBeans = resolveMultipleBeans(descriptor);
        if (multipleBeans != null) {
            return multipleBeans;
        }

        //3.1 寻找依赖的候选项
        Map<String, Object> matchingBeans = findAutowireCandidates(descriptor.getDependencyType(), descriptor);
        if(matchingBeans.isEmpty()){
            //如果依赖项是必须的，抛错
            if(this.autowireCandidateResolver.isRequired(descriptor)){
                throw new BeansException("依赖类型为" + descriptor.getDependencyType().getName() + "的候选项不存在");
            }
            return null;
        }

        //3.2 获得唯一的候选者
        String autowiredBeanName;
        Object instanceCandidate;
        if (matchingBeans.size() > 1) {
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            if (autowiredBeanName == null && this.autowireCandidateResolver.isRequired(descriptor)) {
                throw new BeansException("依赖类型为" + descriptor.getDependencyType().getName() + "的候选项不是唯一的");
            }
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }else{
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

        //3.3  获取依赖项实例
        Object result;
        if(instanceCandidate instanceof Class){
            //如果候选者是Class类型，则创建Bean实例
            result = getBean(autowiredBeanName);
        }else {
            result = instanceCandidate;
        }
        return result;
    }


    //解析集合类型
    private Object resolveMultipleBeans(DependencyDescriptor descriptor) {
        Class<?> type = descriptor.getDependencyType();
        //处理Collection类型
        if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            //获取集合元素的类型（不考虑多层嵌套的情况）
            Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
            Map<String, Object> matchingBeans = findAutowireCandidates(elementType, new MultiElementDescriptor(descriptor));

            //类型转换，比如List类型会转成ArrayList
            return getTypeConverter().convertIfNecessary(matchingBeans.values(), type);
        }
        //处理Map类型
        else if (Map.class == type) {
            ResolvableType mapType = descriptor.getResolvableType().asMap();
            //key必须是String类型
            Class<?> keyType = mapType.resolveGeneric(0);
            if (String.class != keyType) {
                return null;
            }

            Class<?> valueType = mapType.resolveGeneric(1);
            return findAutowireCandidates(valueType, new MultiElementDescriptor(descriptor));
        }
        return null;
    }


    //寻找符合条件的候选项集合
    private Map<String,Object> findAutowireCandidates(Class<?> requiredType, DependencyDescriptor descriptor) {
        Map<String, Object> result = new LinkedHashMap<>();

        //1. 查找预设的依赖项
        for(Class<?> autowiringType: this.resolvableDependencies.keySet()){
            if(autowiringType.isAssignableFrom(requiredType)){
                Object autowiringValue = this.resolvableDependencies.get(autowiringType);
                if(requiredType.isInstance(autowiringValue)){
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }

        //2. 查找指定类型的所有Bean的名称（包括父容器）
        List<String> candidateNames = getBeanNamesForType(requiredType);
        if(getParentBeanFactory() != null){
            candidateNames.addAll(getParentBeanFactory().getBeanNamesForType(requiredType));
        }

        for (String candidate : candidateNames) {
            //进一步检查是否满足相关要求，比如声明了@Qualifier的情况
            if(isAutowireCandidate(candidate, descriptor)){
                //如果依赖项是集合类型，或者候选项是已注册的单例，直接将Bean加入列表
                if(descriptor instanceof MultiElementDescriptor || containsSingleton(candidate)){
                    result.put(candidate, getBean(candidate));
                }
                //否则添加Class对象
                else{
                    result.put(candidate, getType(candidate));
                }
            }
        }
        return result;
    }


    //主要是为 @Qualifier 注解服务的，如果没有声明该注解，可简单认为返回true
    protected boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor){
        String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
        BeanDefinitionHolder holder = null;
        if (containsBeanDefinition(beanDefinitionName)) {
            holder = new BeanDefinitionHolder(getMergedBeanDefinition(beanName), beanName);
        } else if (containsSingleton(beanName)) {
            holder = new BeanDefinitionHolder(new RootBeanDefinition(getType(beanName)), beanName);
        }

        if(holder != null){
            return this.autowireCandidateResolver.isAutowireCandidate(holder, descriptor);
        }
        return true;
    }


    //从众多候选项找到一个最符合条件的合格项
    protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
        //查找primary标记为true的候选项
        String primaryCandidate = determinePrimaryCandidate(candidates);
        if(primaryCandidate != null){
            return primaryCandidate;
        }

        //查找优先级最高的候选项（略）

        //查找预设的依赖项，或者beanName与字段/参数名相同的候选项
        for (Map.Entry<String, Object> entry : candidates.entrySet()) {
            String candidateName = entry.getKey();
            Object beanInstance = entry.getValue();
            if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
                    (candidateName != null && candidateName.equals(descriptor.getDependencyName()))) {
                return candidateName;
            }
        }
        return null;
    }


    protected String determinePrimaryCandidate(Map<String, Object> candidates) {
        for (Map.Entry<String, Object> entry : candidates.entrySet()) {
            String candidateBeanName = entry.getKey();
            if(containsBeanDefinition(candidateBeanName) && getBeanDefinition(candidateBeanName).isPrimary()){
                return candidateBeanName;
            }
        }
        return null;
    }


    // ------------------------------- ConfigurableBeanFactory接口

    @Override
    public void registerResolvableDependency(Class<?> dependencyType, Object autowiredValue) {
        if (!dependencyType.isInstance(autowiredValue)) {
            throw new IllegalArgumentException(autowiredValue + "不是依赖类型[" + dependencyType.getName() + "]的实例");
        }
        this.resolvableDependencies.put(dependencyType, autowiredValue);
    }


    @Override
    public void preInstantiateSingletons() {
        //实例化所有非懒加载的单例Bean
        List<String> beanNames = new ArrayList<>(beanDefinitionNames);
        for(String beanName : beanNames){
            RootBeanDefinition bd = getMergedBeanDefinition(beanName);
            if(bd.isSingleton() && !bd.isLazyInit()){
                getBean(beanName);
            }
        }

        //所有单例Bean实例化后，触发SmartInitializingSingleton回调
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                ((SmartInitializingSingleton) singletonInstance).afterSingletonsInstantiated();
            }
        }
    }


    @Override
    public void destroySingletons() {
        super.destroySingletons();
        this.manualSingletonNames.clear();
    }

    // ------------------------------- 内部类
    //集合类型依赖项的描述符，只考虑嵌套一层的情况，比如List<Foo>
    private static class MultiElementDescriptor extends DependencyDescriptor {

        public MultiElementDescriptor(DependencyDescriptor original) {
            super(original);
            increaseNestingLevel();
        }
    }


    private class DependencyObjectProvider implements ObjectProvider<Object> {
        private final DependencyDescriptor descriptor;
        private final String beanName;

        public DependencyObjectProvider(DependencyDescriptor descriptor, String beanName) {
            //descriptor的类型为ObjectProvider<T>，需要获得T的类型，因此嵌套层级+1
            descriptor.increaseNestingLevel();
            this.descriptor = descriptor;
            this.beanName = beanName;
        }

        @Override
        public Object getObject() throws BeansException {
            return doResolveDependency(this.descriptor);
        }
    }


    private class Jsr330DependencyProvider extends DependencyObjectProvider implements Provider<Object> {

        public Jsr330DependencyProvider(DependencyDescriptor descriptor, String beanName) {
            super(descriptor, beanName);
        }

        @Override
        public Object get() throws BeansException {
            return getObject();
        }
    }
}
